/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "SoundSource.h"
#include "SoundListener.h"
#include <noise/Manager.h>

#include <boost/bind.hpp>

namespace liba {
namespace lib3d {
namespace node {

const Atom SoundSource::node_name("SoundSource");
const Atom SoundSource::play_command_name("Play");
const Atom SoundSource::pend_command_name("Pend");
const Atom SoundSource::stop_command_name("Stop");

SoundSource::SoundSource(const Atom & name, Node * parent ):
	Node( name, parent ),
	last_pos( 0,0,0 ),
	min_distance( 1 ),
	max_distance( 1E9 ),
	channel_type( noise::hard::Hardware::channel_type_3d ),
	loop( true )
{
	playCommand.Invoked().connect(boost::bind(&SoundSource::executePlayCommand, this));
	this->RegisterCommand(play_command_name, &playCommand);
	stopCommand.Invoked().connect(boost::bind(&SoundSource::executeStopCommand, this));
	this->RegisterCommand(stop_command_name, &stopCommand);
}

SoundSource::SoundSource(const SoundSource & other)
:	Node( other ),
	last_pos( other.last_pos ),
	sound_name(other.sound_name),
	min_distance( other.min_distance ),
	max_distance( other.max_distance ),
	channel_type( other.channel_type ),
	loop( other.loop )
	
{
	channel.reset(noise::Manager::get_hardware()->create_static_channel( channel_type, sound_name ));
	channel->FindProperty<bool>(noise::hard::Channel::loop_name)->Set(loop);
	channel->FindProperty<double>(noise::hard::Channel::minimum_distance_name)->Set(min_distance);
	channel->FindProperty<double>(noise::hard::Channel::maximum_distance_name)->Set(max_distance);
	update_position(1);	
}
	
SoundSource * SoundSource::Clone()
{
	return new SoundSource(*this);
}

void SoundSource::update_position(double delta_time)
{
	Vector pos = GetGlobalMatrix().shift();
	Vector speed = (delta_time == 0) ? Vector(0,0,0) : Vector((pos - last_pos)/delta_time);
	last_pos = pos;

	channel->FindProperty<lib3d::Vector>(noise::hard::Channel::position_name)->Set(pos);
	channel->FindProperty<lib3d::Vector>(noise::hard::Channel::speed_name)->Set(speed);
}

void SoundSource::LifeCycle(time_float delta_time)
{
	update_position(delta_time);
	/*if( !SoundListener::is_listening(this) )
	{
		channel->ChangeAttribute( noise::hard::Channel::volume_name, double(0) );
	}
	else*/
	{
///		channel->ChangeAttribute( noise::hard::Channel::volume_name, double(1) );

	}

	/*if( channel->is_finished() )
	{
		channel->ChangeAttribute( noise::hard::Channel::loop_name, double(1) );
		channel->play( noise::Manager::create_datasource( sound_name ) );
	}*/
}

bool SoundSource::on_attribute( xml::Provider * prov, const std::string & name, const std::string & value )
{
	if( name == "Sound" )
		return converter::convert( value, &sound_name );
	if( name == "ChannelType" )
		return converter::convert( value, &channel_type );
	if( name == "Loop" )
		return converter::convert( value, &loop );
	if( name == "MinDistance" )
		return converter::convert( value, &min_distance );
	if( name == "MaxDistance" )
		return converter::convert( value, &max_distance );
	return Node::on_attribute( prov, name, value );
}

bool SoundSource::on_end_node( xml::Provider * prov )
{
	channel.reset(noise::Manager::get_hardware()->create_static_channel( channel_type, sound_name ));
	channel->FindProperty<bool>(noise::hard::Channel::loop_name)->Set(loop);
	channel->FindProperty<double>(noise::hard::Channel::minimum_distance_name)->Set(min_distance);
	channel->FindProperty<double>(noise::hard::Channel::maximum_distance_name)->Set(max_distance);
	last_pos = GetGlobalMatrix().shift();
	update_position(1);
	return Node::on_end_node( prov );
}

void SoundSource::executePlayCommand()
{
	channel->stop();
	channel->play( 0 );
}

void SoundSource::executeStopCommand()
{
	channel->stop();
}

} // node
} // namespace lib3d
} // namespace liba

